
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WMVStream
{
    internal class CheckResult
    {
        public CheckResult ()
        {
        }

        public override string ToString ()
        {
            string res = string.Format (
@"CheckResult object
isOnline: {0}
name: {1}
sourceProtocol: {2}
bandWidth: {3}
bitRate: {4}
imageSourceWidth: {5}
imageSourceHeight: {6}
frameRate: {7}
encodedFrameRate: {8}
framesSkipped: {9}
receivedPackets: {10}
recoveredPackets: {11}
lostPackets: {12}
receptionQuality: {13}",
                isOnline,
                name,
                sourceProtocol,
                bandWidth,
                bitRate,
                imageSourceWidth,
                imageSourceHeight,
                frameRate / 100.0,
                encodedFrameRate,
                framesSkipped,
                receivedPackets,
                recoveredPackets,
                lostPackets,
                receptionQuality
                );

            return res;
        }

        public int bandWidth = 0;

        public int bitRate = 0;

        /// <summary>
        /// The frameRate property retrieves the current video frame rate in 
        /// frames per hundred seconds. For example, a value of 2998 indicates 
        /// 29.98 frames per second. 
        /// </summary>
        public int frameRate = 0;

        /// <summary>
        /// The encodedFrameRate property retrieves the video frame rate 
        /// specified by the content author in frames per second
        /// </summary>        
        public int encodedFrameRate = 0;

        /// <summary>
        /// The framesSkipped property retrieves the total number of frames 
        /// skipped during playback.
        /// </summary>
        public int framesSkipped = 0;

        /// <summary>
        /// The lostPackets property retrieves the number of packets lost.
        /// </summary>
        public int lostPackets = 0;

        /// <summary>
        /// The receivedPackets property retrieves the number of packets received.
        /// </summary>
        public int receivedPackets = 0;

        /// <summary>
        /// The receptionQuality property retrieves the percentage of packets 
        /// received in the last 30 seconds.
        /// </summary>
        public int receptionQuality = 0;

        /// <summary>
        /// The recoveredPackets property retrieves the number of recovered 
        /// packets.
        /// </summary>
        public int recoveredPackets = 0;

        /// <summary>
        /// The sourceProtocol property retrieves the source protocol used to 
        /// receive data.
        /// </summary>
        public string sourceProtocol = "";


        /// <summary>
        /// The ImageSourceHeight property retrieves the height of the current 
        /// media item in pixels.
        /// </summary>
        public int imageSourceHeight = 0;

        /// <summary>
        /// The imageSourceWidth property retrieves the width of the 
        /// current media item in pixels.
        /// </summary>
        public int imageSourceWidth = 0;

        /// <summary>
        /// The name property specifies or retrieves the name of the media item.
        /// </summary>
        public string name = "";

        /// <summary>
        /// Indication that the channel was successfully opened
        /// </summary>
        public bool isOnline = false;
    }

    internal class StreamChecker : IDisposable
    {
        AutoResetEvent waitEvent;
        WMPLib.WindowsMediaPlayer player;

        public StreamChecker ()
        {
            waitEvent = new AutoResetEvent (false);
            player = new WMPLib.WindowsMediaPlayer ();
            player.PlayStateChange += new WMPLib._WMPOCXEvents_PlayStateChangeEventHandler (player_PlayStateChange);
        }

        CheckResult scanResult;
        /// <summary>
        /// Checks channel workability
        /// </summary>
        /// <param name="streamURL">Channel URL</param>
        /// <param name="openTimeout">Channel opening latency time</param>
        /// <param name="readTimeout">Channel content playback time 
        /// (for statistic calculation)</param>
        /// <returns>Check results</returns>
        public CheckResult Check (string streamURL, TimeSpan openTimeout, TimeSpan readTimeout)
        {
            CheckResult result = new CheckResult ();
            scanResult = result;
            try
            {

                try
                {
                    waitEvent.Reset ();
                    player.settings.mute = true;
                    player.settings.autoStart = true;
                    player.URL = streamURL;

                    bool waitResult = waitEvent.WaitOne (openTimeout, false);
                    if (!waitResult) return result;
                    Thread.Sleep (readTimeout);


                    scanResult.bandWidth = player.network.bandWidth;
                    scanResult.bitRate = player.network.bitRate;
                    scanResult.frameRate = player.network.frameRate;
                    scanResult.encodedFrameRate = player.network.encodedFrameRate;
                    scanResult.framesSkipped = player.network.framesSkipped;
                    scanResult.receivedPackets = player.network.receivedPackets;
                    scanResult.recoveredPackets = player.network.recoveredPackets;
                    scanResult.lostPackets = player.network.lostPackets;
                    scanResult.receptionQuality = player.network.receptionQuality;
                    scanResult.sourceProtocol = player.network.sourceProtocol;

                    scanResult.imageSourceWidth = player.currentMedia.imageSourceWidth;
                    scanResult.imageSourceHeight = player.currentMedia.imageSourceHeight;

                    return result;
                }
                finally
                {
                    player.settings.mute = false;
                    player.close ();
                }

            }
            catch (Exception ex)
            {
                //MessageBox.Show (ex.Message);
            }

            return result;
        }


        void player_PlayStateChange (int NewState)
        {
            /*
            0 Undefined Windows Media Player is in an undefined state. 
            1 Stopped Playback of the current media item is stopped. 
            2 Paused Playback of the current media item is paused. When a media item is paused, resuming playback begins from the same location. 
            3 Playing The current media item is playing. 
            4 ScanForward The current media item is fast forwarding. 
            5 ScanReverse The current media item is fast rewinding. 
            6 Buffering The current media item is getting additional data from the server. 
            7 Waiting Connection is established, but the server is not sending data. Waiting for session to begin. 
            8 MediaEnded Media item has completed playback.  
            9 Transitioning Preparing new media item. 
            10 Ready Ready to begin playing. 
            11 Reconnecting Reconnecting to stream. 
             */

            try
            {
                switch (NewState)
                {
                    case 3:
                        scanResult.isOnline = true;
                        waitEvent.Set ();
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show (ex.Message);
            }
        }



        #region IDisposable Members

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose ()
        {
            Dispose (true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue 
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize (this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose (bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.                    
                    if (null != waitEvent) waitEvent.Close ();
                    if (null != player) player.close ();
                    player = null;
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here.
                // If disposing is false, 
                // only the following code is executed.
                //CloseHandle(handle);
                //handle = IntPtr.Zero;			
            }
            disposed = true;
        }

        bool disposed = false;

        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method 
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~StreamChecker ()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose (false);
        }

        #endregion
    }
}
